import socket
import cPickle
import thread
from threading import Semaphore, Thread, Lock
import time
import logging
import os
import sys
import getopt
import signal
import re



def initNetworking(__bind = '', __recvBcast = False):
    """ Networking setup.
        Start at least one thread for receiving. A second thread when
        receiving on '<breadcast>' too.
    """
    global __stdSock, __recvSock, __listening, __bcastRecvThread, __log
    
    # setup the standard socket (receiving & sending)
    __stdSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    __stdSock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    __stdRecvThread = thread.start_new_thread(__recvLoop, (__stdSock, __recvQueueSem))
    try:
        __stdSock.bind((__bind, defPort))
    except socket.error, e:
        __log.error('Error: %s' % e)

    # setup a broadcast socket (receiving)
    if __bind != '' and __recvBcast:
        __bcastSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        __bcastSock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        try:
            __bcastSock.bind(('<broadcast>', defPort))
            __bcastRecvThread = thread.start_new_thread(__recvLoop, (__bcastSock, __recvQueueSem))
        except socket.error, e:
            __log.error('Error: %s' % e)
            __log.error('- still running, but unable to receive broadcast messages')

def stopNetworking():
    """ Cleanup networking.
        shutwdown sockets nicely etc.
    """
    global __listening, __stdSock, __bcastSock, __log
    __listening = False
    try:
        __stdSock.close()
    except socket.error, e:
        __log.error('stopNetworking: could not shutdown stdSock...')
        __log.debug('stopNetworking: %s' % e)
    if __bcastSock:
        try:
            __bcastSock.close()
        except socket.error, e:
            __log.error('stopNetworking: could not shutdown bcastSock...')
            __log.debug('stopNetworking: %s' % e)
    
def sendMsg(msg):
    """ Send a message to 'msg.dst' or 'defAddr'.
        The message is converted using the cPickle module.
    """
    global __stdSock, __log
    rawMsg = cPickle.dumps(msg)
    __log.debug('sendMsg: %s' % msg)
    dst = defAddr
    if msg.dst != None:
        dst = msg.dst
    __log.debug('sendMsg: sending %d bytes to %s:%d' % (len(rawMsg), dst, defPort))
    try:
        size = __stdSock.sendto(rawMsg, (dst, defPort))
        if size != len(rawMsg):
            __log.debug('sendMsg: tried to send %d bytes, %d sent...' % (len(rawMsg), size))
    except socket.error, e:
        __log.error('sendMsg: Failed to send message...')
        __log.error('sendMsg: Error: %s' % e)

def recvMsg():
    """ A blocking receive function.
        Wait for messages in the __recvQueue. When the queue is empty, block until
        one of the receive threads releases the blocking Semaphore.
    """
    global __recvQueue, __stdSock, __recvQueueSem, __log
    msg = None
    __log.debug('recvMsg(): waiting for message...')
    while not msg:
        if len(__recvQueue) > 0:
            msg =  __recvQueue.pop(0)
        else:
            __recvQueueSem.acquire()        
    __log.debug('recvMsg(): returning %s - %d msgs left' % (msg, len(__recvQueue)))
    return msg

def __sockRecvMsg(__sock):
    """ Function used internally for receiving a msg on a socket.
        This function is used by the standard receive thread and when
        running, the broadcast receive thread.
        Converting raw data to Pythong Objects is handled by cPickle.
    """
    global __log
    src = None
    msg = None
    rawMsg = None
    try:
        msgTuple = __sock.recvfrom(SOCK_BUF_SIZE)
        if msgTuple:
            (rawMsg, addr) = msgTuple
            if addr:
                (src, port) = addr
    except socket.timeout, t:
        pass
    if rawMsg:
        try:
            msg = cPickle.loads(rawMsg)
            msg.src = src
        except ValueError, ve:
            __log.error('Could not unpickle:\n%s\n' % rawMsg)
        except cPickle.UnpicklingError, pe:
            __log.error('__sockRecvMsg: could not read message: %s' % pe)
    return msg

def __recvLoop(__sock, __sem):
    """ Thread function for receiving messages.
        Started by the standard receive thread and when running, the
        broadcast receive thread.
    """
    global __recvQueue, __listening, __log
    __log.debug('__recvLoop(): started...')
    while __listening:
        msg = __sockRecvMsg(__sock)
        __log.debug('__recvLoop(): received %s' % msg)
        __recvQueue.append(msg)
        __sem.release()
